package com.chs.recoginition.core.utils;

import com.chs.recoginition.core.exception.ApplicationException;
import com.chs.recoginition.core.exception.constant.ExceptionConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class BaseUtil {

    private static Random _random = new Random();

    public static BigDecimal minusOneBD = new BigDecimal("-1");

    // 登陆有效时间
    public static int loginContinueTime = 60 * 60 * 24;

    // 票据key
    public static String tokenKey = "cn|/?n*()&^%$sdf3r98jhgjhAHHH89sf*094#@3=-@1·324";
    public static String tokenNoPwdKey = "cn|/?n*()&^%$sdf3r98jhgjhAWWW89sf*094#@3=-@1·324";

    public static String USER_HEAD_KEY = "jrdc_!user!_"; // 用户登陆key
    public static String LOGIN_CODE_KEY = "login_code_";// 登录验证码

    public static SimpleDateFormat sdf1 = new SimpleDateFormat("HH:mm");
    public static SimpleDateFormat sdf2 = new SimpleDateFormat("MMdd");
    public static SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd");
    public static SimpleDateFormat sdf4 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static SimpleDateFormat sdf5 = new SimpleDateFormat("yyyyMM");
    public static SimpleDateFormat sdf6 = new SimpleDateFormat("yyyyMMdd");
    public static SimpleDateFormat sdf7 = new SimpleDateFormat("HH:mm:ss");
    public static SimpleDateFormat sdf8 = new SimpleDateFormat("yyyyMMddHHmmssS");
    public static SimpleDateFormat sdf9 = new SimpleDateFormat("MM月dd日 HH:mm");
    private static String[] parsePatterns = {"yyyy-MM-dd", "yyyy年MM月dd日",
            "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
            "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd", "yyyy-MM-ddTHH:mm:ss"};

    /**
     * 判断字符串是否为日期格式
     */
    public static Date parseDate(String string) {
        if (isEmpty(string)) {
            return null;
        }
        Date date = null;
        try {
            date = DateUtils.parseDate(string, parsePatterns);
        } catch (ParseException e) {
            return null;
        }
        return date;
    }

    /**
     * 保存各种已处理的id
     */
    public static Map<String, List<Object>> idLists = Collections.synchronizedMap(new HashMap<String, List<Object>>());

    public static void addToIdList(String key, Object id) {
        List<Object> list = idLists.get(key);
        if (list == null) {
            list = new ArrayList<>();
        }
        if (list.size() >= 1000) {
            list = new ArrayList<>(list.subList(100, list.size()));
        }
        list.add(id);
        idLists.put(key, list);
    }

    public static boolean containKeyForIdList(String key, Object content) {
        if (!idLists.containsKey(key)) {
            return false;
        }
        List<Object> list = idLists.get(key);
        return list != null && list.contains(content);
    }

    /**
     * 在min-max生成numSize不同的数字
     *
     * @param min
     * @param max
     * @param numSize
     * @return
     */
    public static HashSet<Integer> randomNumber(int min, int max, int numSize) {
        HashSet<Integer> set = new HashSet<>();
        if (numSize > 0 && numSize <= max - min + 1) {
            if (numSize == max - min + 1) {
                for (int i = min; i <= max; i++) {
                    set.add(i);
                }
            } else {
                do {
                    int ranInt = random(min, max);
                    set.add(ranInt);
                } while (set.size() != numSize);
            }
        }
        return set;
    }

    /**
     * 在min-max生成numSize不同的数字,不包含在list里面
     *
     * @param min
     * @param max
     * @param numSize
     * @return
     */
    public static HashSet<Integer> randomNumber(int min, int max, int numSize, List<Integer> list) {
        HashSet<Integer> set = new HashSet<>();
        if (numSize > 0 && numSize <= max - min - list.size() + 1) {
            if (numSize == max - min - list.size() + 1) {
                for (int i = min; i <= max; i++) {
                    if (!list.contains(i)) {
                        set.add(i);
                    }
                }
            } else {
                do {
                    int ranInt = random(min, max);
                    if (!list.contains(ranInt)) {
                        set.add(ranInt);
                    }
                } while (set.size() != numSize);
            }
        }
        return set;
    }

    /**
     * 生成随机数[min, max]
     *
     * @param min
     * @param max
     * @return
     */
    public static int random(int min, int max) {
        return _random.nextInt(max - min + 1) + min;
    }

    /**
     * 取出字符串里面的手机号码
     *
     * @param str
     * @return
     */
    public static List<String> getPhoneNum(String str) {
        Pattern p = Pattern.compile("0[^0]\\d{1,2}[-]?\\d{7,8}|13\\d{9}|15\\d{9}");
        Matcher matcher = p.matcher(str);
        List<String> result = new ArrayList<>();
        while (matcher.find()) {
            String temp = matcher.group();
            if (!isEmpty(temp)) {
                result.add(temp);
            }
        }
        return result;
    }

    /**
     * 取出字符串里面的最后一个数字
     *
     * @param content
     * @return
     */
    public static String getLastNumByString(String content) {
        String result = null;
        Pattern pattern = Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            result = matcher.group(0);
        }
        return result;
    }

    /**
     * 判断手机号码
     *
     * @param mobiles
     * @return
     */
    public static boolean isMobileNO(String mobiles) {
        Pattern p = Pattern.compile("^\\d{11}$");
        Matcher m = p.matcher(mobiles);
        return m.matches();
    }

    /**
     * 判断是否是数字
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        return isNum.matches();
    }

    /**
     * 判断是否是数字(正负整数、小数)
     *
     * @param str
     * @return
     */
    public static boolean isNumer(String str) {
        if (str == null || str.length() == 0) {
            return false;
        }
        Pattern pattern = Pattern.compile("^-?\\d+(\\.\\d+)?$");
        Matcher isNum = pattern.matcher(str);
        return isNum.matches();
    }

    /**
     * 判断字符串是否为空
     *
     * @param str
     * @return
     */
    public static boolean isEmpty(String str) {
        return str == null || "".equals(str.trim());
    }

    /**
     * 判断数组是否为空
     *
     * @param list
     * @return
     */
    public static <T> boolean isEmpty(List<T> list) {
        return list == null || list.size() == 0;
    }

    /**
     * string[]转List<Integer>
     *
     * @param strs
     * @return
     */
    public static List<Integer> array2List(String[] strs) {
        List<Integer> result = new ArrayList<>();
        for (String temp : strs) {
            result.add(Integer.parseInt(temp));
        }
        return result;
    }

    /**
     * String[]转String
     *
     * @param strs
     * @return
     */
    public static String array2Str(Object[] strs, String separator) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < strs.length; i++) {
            sb.append(strs[i]);
            if (i < strs.length - 1) {
                sb.append(separator);
            }
        }
        return sb.toString();
    }

    /**
     * Object[][]转String
     *
     * @param strs
     * @return
     */
    public static String array2Str(Object[][] strs, String separator) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < strs.length; i++) {
            for (int j = 0; j < strs[i].length; j++) {
                sb.append(strs[i][j]);
                if (i != strs.length && j != strs[i].length - 1) {
                    sb.append(separator);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 打印详细错误日志
     *
     * @param e
     * @return
     */
    public static String logError(Exception e) {
        StringBuilder sb = new StringBuilder(e.toString());
        for (StackTraceElement ste : e.getStackTrace()) {
            sb.append("\r\n");
            sb.append(ste.toString());
        }
        return sb.toString();
    }

    /**
     * 是否是同一天
     *
     * @param cal1
     * @param cal2
     * @return
     */
    public static boolean isSameDay(Calendar cal1, Calendar cal2) {
        boolean isSameYear = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
        boolean isSameMonth = isSameYear && cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
        boolean isSameDate = isSameMonth && cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH);
        return isSameDate;
    }

    /**
     * 获取上个月第一天 yyyy-MM-dd
     *
     * @return
     */
    public static String getStartDayOfLastMonth() {
        Calendar instance = Calendar.getInstance();
        instance.set(Calendar.DAY_OF_MONTH, 1);// 天设为一个月的第一天
        instance.add(Calendar.MONTH, -1);// 月份-1
        return sdf3.format(instance.getTime());
    }

    /**
     * 获取上个月最后一天 yyyy-MM-dd
     *
     * @return
     */
    public static String getEndDayOfLastMonth() {
        Calendar instance = Calendar.getInstance();
        instance.set(Calendar.DAY_OF_MONTH, 1);// 天设为一个月的第一天
        instance.add(Calendar.DAY_OF_MONTH, -1);// 上个月最后一天
        return sdf3.format(instance.getTime());
    }

    /**
     * 获取月第一天 yyyy-MM-dd
     *
     * @return
     */
    public static String getStartDayOfMonth(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.DAY_OF_MONTH, 1);// 天设为一个月的第一天
        return sdf3.format(instance.getTime());
    }

    /**
     * 获取月最后一天 yyyy-MM-dd
     *
     * @return
     */
    public static Date getEndDayOfMonthReturnDate(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.DAY_OF_MONTH, 1);// 天设为一个月的第一天
        instance.add(Calendar.MONTH, 1);// 月份+1
        instance.add(Calendar.DAY_OF_MONTH, -1);// 本月最后一天
        return instance.getTime();
    }

    /**
     * 获取月最后一天 yyyy-MM-dd
     *
     * @return
     */
    public static String getEndDayOfMonth(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.DAY_OF_MONTH, 1);// 天设为一个月的第一天
        instance.add(Calendar.MONTH, 1);// 月份+1
        instance.add(Calendar.DAY_OF_MONTH, -1);// 本月最后一天
        return sdf3.format(instance.getTime());
    }

    /**
     * 获取月倒数第二天 yyyy-MM-dd
     *
     * @return
     */
    public static String getEnd2DayOfMonth(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.DAY_OF_MONTH, 1);// 天设为一个月的第一天
        instance.add(Calendar.MONTH, 1);// 月份+1
        instance.add(Calendar.DAY_OF_MONTH, -2);// 本月最后一天
        return sdf3.format(instance.getTime());
    }

    /**
     * 获取N天前 (Date)
     *
     * @return
     */
    public static Date getNDay(Date date, int N) {
        Calendar curTime = Calendar.getInstance();
        curTime.setTimeInMillis(date.getTime() - N * 24 * 60 * 60 * 1000L);
        return curTime.getTime();
    }

    /**
     * 获取当前的年月 yyyyMM
     *
     * @return
     */
    public static String getCurMonth() {
        return sdf5.format(new Date());
    }

    /**
     * 获取当前日期yyyyMMdd转整形
     *
     * @return
     */
    public static int getCurDay() {
        return Integer.parseInt(sdf6.format(new Date()));
    }

    /**
     * 获取当前日期 yyyy-MM-dd
     *
     * @return
     */
    public static String getCurDayStr() {
        return sdf3.format(new Date());
    }

    /**
     * 获取现在的时分秒，加上偏移量
     *
     * @param offsetSecond
     * @return
     */
    public static String getCurHourMinuteSecond(int offsetSecond) {
        Calendar now = Calendar.getInstance();
        now.add(Calendar.SECOND, offsetSecond);
        return sdf7.format(now.getTime());
    }

    /**
     * 判断毫秒数是不是今天
     *
     * @param l
     * @return
     */
    public static boolean isDay(long l) {
        Calendar cur = Calendar.getInstance();
        Calendar taget = Calendar.getInstance();
        taget.setTimeInMillis(l);
        return cur.get(Calendar.DAY_OF_YEAR) == taget.get(Calendar.DAY_OF_YEAR);
    }


    /**
     * 日期差
     *
     * @param fDate
     * @param oDate
     * @return
     */
    public static int getIntervalDays(Calendar fDate, Calendar oDate) {
        if (null == fDate || null == oDate) {
            return -1;
        }
        Calendar endCal = Calendar.getInstance();
        endCal.set(Calendar.YEAR, oDate.get(Calendar.YEAR));
        endCal.set(Calendar.MONTH, oDate.get(Calendar.MONTH));
        endCal.set(Calendar.DAY_OF_MONTH, oDate.get(Calendar.DAY_OF_MONTH));
        endCal.set(Calendar.HOUR_OF_DAY, fDate.get(Calendar.HOUR_OF_DAY));
        endCal.set(Calendar.MINUTE, fDate.get(Calendar.MINUTE));
        endCal.set(Calendar.SECOND, fDate.get(Calendar.SECOND));
        endCal.set(Calendar.MILLISECOND, fDate.get(Calendar.MILLISECOND));
        long intervalMilli = endCal.getTimeInMillis() - fDate.getTimeInMillis();
        return (int) (intervalMilli / (24 * 60 * 60 * 1000));
    }

    /**
     * 获取两天之间的所有日期
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static List<String> getAllDayBetweenBeginAndEnd(Calendar beginDate, Calendar endDate) {
        List<String> result = new ArrayList<>();
        if (isSameDay(beginDate, endDate)) {
            return result;
        }
        result.add(sdf3.format(beginDate.getTime()));
        while (true) {
            beginDate.add(Calendar.DAY_OF_MONTH, 1);
            if (!isSameDay(beginDate, endDate)) {
                result.add(sdf3.format(beginDate.getTime()));
            } else {
                break;
            }
        }
        return result;
    }

    /**
     * 获取两天之间的所有日期, 包括最后一天
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static List<String> getAllDayBetweenBeginAndEnd_1(Calendar beginDate, Calendar endDate) {
        List<String> result = new ArrayList<>();
        result.add(sdf3.format(beginDate.getTime()));
        if (isSameDay(beginDate, endDate)) {
            return result;
        }
        while (true) {
            beginDate.add(Calendar.DAY_OF_MONTH, 1);
            if (!isSameDay(beginDate, endDate)) {
                result.add(sdf3.format(beginDate.getTime()));
            } else {
                break;
            }
        }
        result.add(sdf3.format(endDate.getTime()));
        return result;
    }

    /**
     * 获取两天之间的所有日期(包括第一天的昨天)
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static List<String> getAllDayBetweenBegin_1AndEnd(Calendar beginDate, Calendar endDate) {
        List<String> result = new ArrayList<>();
        beginDate.add(Calendar.DAY_OF_MONTH, -1);
        result.add(sdf3.format(beginDate.getTime()));
        beginDate.add(Calendar.DAY_OF_MONTH, 1);
        result.add(sdf3.format(beginDate.getTime()));
        while (true) {
            beginDate.add(Calendar.DAY_OF_MONTH, 1);
            if (!isSameDay(beginDate, endDate)) {
                result.add(sdf3.format(beginDate.getTime()));
            } else {
                break;
            }
        }
        return result;
    }

    /**
     * 获取字符串的真实长度
     *
     * @param value
     * @return
     */
    public static int getStringlength(String value) {
        int valueLength = 0;
        String chinese = "[\u4e00-\u9fa5]";
        for (int i = 0; i < value.length(); i++) {
            String temp = value.substring(i, i + 1);
            if (temp.matches(chinese)) {
                valueLength += 2;
            } else {
                valueLength += 1;
            }
        }
        return valueLength;
    }

    public static void main(String[] args) throws SocketException, UnknownHostException, ParseException {
        Calendar beginCal = Calendar.getInstance();
        beginCal.setTime(sdf4.parse("2017-07-05 00:00:00"));
        Calendar endCal = Calendar.getInstance();
        endCal.setTime(sdf4.parse("2017-07-12 00:00:00"));
        List<String> result = getAllDayBetweenBeginAndEnd(beginCal, endCal);
        for (String str : result) {
            System.out.println(str);
        }
        //
    }

    /**
     * 获取当前时间所属的时间段
     *
     * @param timeQuantumList 时间段集合 [起始时间小时,起始时间分钟,结束时间小时,结束时间分钟]
     * @return 当前时间所属时间段在timeQuantumList里的索引，-1表示当前时间不在这些时间段里
     */
    public static int getNowTimeQuantum(List<Integer[]> timeQuantumList) {
        long now = System.currentTimeMillis();
        Calendar startTime = Calendar.getInstance();
        Calendar endTime = Calendar.getInstance();
        for (int i = 0; i < timeQuantumList.size(); i++) {
            Integer[] arr = timeQuantumList.get(i);
            startTime.set(Calendar.HOUR_OF_DAY, arr[0]);
            startTime.set(Calendar.MINUTE, arr[1]);
            endTime.set(Calendar.HOUR_OF_DAY, arr[2]);
            endTime.set(Calendar.MINUTE, arr[3]);
            if (endTime.getTimeInMillis() < startTime.getTimeInMillis()) {
                if (now > startTime.getTimeInMillis() || now <= endTime.getTimeInMillis()) {
                    return i;
                }
            } else {
                if (now > startTime.getTimeInMillis() && now <= endTime.getTimeInMillis()) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 判断字符串是否包含汉字
     *
     * @param str
     * @return
     */
    public static boolean isContainChinese(String str) {
        if (str == null) {
            return false;
        }
        Pattern pattern = Pattern.compile("[\\u4E00-\\u9FBF]+");
        return pattern.matcher(str.trim()).find();
    }

    /**
     * 获取客户端ip
     *
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 字符串转成int
     *
     * @param changeStr 转换的对象
     * @param minVale   int 范围最小值
     * @param maxValue  int 范围最大值
     * @param message   转成对象名称
     */
    public static Integer stringToInt(String changeStr, Integer minVale, Integer maxValue, String message) {
        Integer changeStrInt = 0;
        try {
            // 先转成成double
            Double changeStrDl = Double.valueOf(changeStr);
            changeStrInt = changeStrDl.intValue();
            if (changeStrInt == null) {
                if (minVale != null && changeStrInt < minVale) {
                    throw new ApplicationException(ExceptionConstant.ILLEGAL_PARAM, message + "小于" + minVale);
                }
                if (maxValue != null && changeStrInt > maxValue) {
                    throw new ApplicationException(ExceptionConstant.ILLEGAL_PARAM, message + "大于" + maxValue);
                }
            }
        } catch (NumberFormatException e) {
            throw new ApplicationException(ExceptionConstant.ILLEGAL_PARAM, message + "请填入正整数");
        }
        return changeStrInt;
    }

    public static LocalDate stringToLocalDate(String changeStr, String message) {
        LocalDate changeLocalDate;
        try {
            changeLocalDate = LocalDateTimeUtils.parseDate(changeStr, "yyyy-MM-dd");
        } catch (DateTimeParseException e) {
            throw new ApplicationException("", message + "时间格式不对，正确时间格式为yyyy-MM-dd");
        }
        return changeLocalDate;
    }

    public static boolean isLinux() {
        return System.getProperty("os.name").toLowerCase().indexOf("linux") >= 0;
    }

    public static boolean isMac() {
        return System.getProperty("os.name").toLowerCase().indexOf("mac") >= 0;
    }

    public static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0;
    }

    public static HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static String getToken() {
        return getRequest().getParameter("token");
    }
}
